Bash - Les arguments (paramètres) et la commande getopts
Il est possible de fournir à un script, sur la ligne de commandes, les arguments nécessaires à son exécution. Ces arguments sont appelés "paramètres".
Il en existe deux catégories : les paramètres positionnels et les paramètres spéciaux.
Table des matières
Ajouter des options avec getop
contrôle les opérateurs utilisés par une commande
Ce sont tous simplement les arguments passés en "paramètres" sur la ligne de commandes, à l'invocation d'un script. Ils sont alors affectés aux variables réservées 1,2,3,...9,10,11,... et peuvent être appelés à l'aide des expressions
$1
,
$2
...
${10}
,
${11}
...
Note : Le shell Bourne est limité aux paramètres de 0 à 9.
Voici un petit script qui se contente d'afficher certains des arguments passés en paramètres en fonction de leur position.
#!/bin/bash
# affiche_param.sh
echo "Le 1er paramètre est : $1"
echo "Le 3ème paramètre est : $3"
echo "Le 10ème paramètre est : ${10}"
echo "Le 15ème paramètre est : ${15}"
Il suffit alors d'invoquer le script en lui passant un certain nombre de paramètres :
./affiche_param.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Le 1er paramètre est : 1
Le 3ème paramètre est : 3
Le 10ème paramètre est : 10
Le 15ème paramètre est : 15
ou encore :
./affiche_param.sh un 2 trois 4 5 6 7 8 9 dix 11 12 13 14 quinze 16 17
Le 1er paramètre est : un
Le 3ème paramètre est : trois
Le 10ème paramètre est : dix
Le 15ème paramètre est : quinze
Si certains paramètres contiennent des caractères spéciaux ou des espaces, il
faut alors les "quoter" :
./affiche_param.sh un 2 "le 3ème" 4 5 6 7 8 9 dix 11 12 13 14 "le 15ème" 16 17
Le 1er paramètre est : un
Le 3ème paramètre est : le 3ème
Le 10ème paramètre est : dix
Le 15ème paramètre est : le 15ème
Ce sont en fait là aussi des variables réservées qui permettent pour certaines d'effectuer des traitements sur les paramètres eux-même. Ces paramètres sont les suivants :
$0
Contient le nom du script tel qu'il a été invoqué
$*
L'ensembles des paramètres sous la forme d'un seul argument
$@
L'ensemble des arguments, un argument par paramètre
$#
Le nombre de paramètres passés au script
$?
Le code retour de la dernière commande
$$
Le PID su shell qui exécute le script
$!
Le PID du dernier processus lancé en arrière-plan
Voici un autre petit script mettant en oeuvre l'ensemble des paramètres spéciaux vus ci-dessus.
#!/bin/bash
# affiche_param_2.sh
# Affichage du nom su script
echo "Le nom de mon script est : $0"
# Affichage du nombre de paramètres
echo "Vous avez passé $# paramètres"
# Liste des paramètres (un seul argument)
for param in "$*"
do
echo "Voici la liste des paramètres (un seul argument) : $param"
done
# Liste des paramètres (un paramètre par argument)
echo "Voici la liste des paramètres (un paramètre par argument) :"
for param in "$@"
do
echo -e " Paramètre : $param"
done
# Affichage du processus
echo "Le PID du shell qui exécute le script est : $$"
# Exécution d'une commande qui s'exécute en arrière-plan
sleep 100 &
# Affichage du processus lancé en arrière-plan
echo "Le PID de la dernière commande exécutée en arrière-plan est : $!"
# Affichage du code retour de la dernière commande "echo"
echo "Le code retour de la commande précédente est : $?"
# Génération d'une erreur
echo "Génération d'une erreur..."
# Affichage de la mauvaise commande
echo "ls /etc/password 2>/dev/null"
ls /etc/password 2>/dev/null
# Affichage du code retour de la dernière commande
echo "Le code retour de la commande précédente est : $?"
exit
Ce qui donne avec l'invocation suivante :
./affiche_param_2.sh 1 2 3 quatre 5 six
Le nom de mon script est : ./affiche_param_2.sh
Vous avez passé 6 paramètres
Voici la liste des paramètres (un seul argument) : 1 2 3 quatre 5 six
Voici la liste des paramètres (un paramètre par argument) :
Paramètre : 1
Paramètre : 2
Paramètre : 3
Paramètre : quatre
Paramètre : 5
Paramètre : six
Le PID du shell qui exécute le script est : 6165
Le PID de la dernière commande exécutée en arrière-plan est : 6166
Le code retour de la commande précédente est : 0
Génération d'une erreur...
ls /etc/password 2>/dev/null
Le code retour de la commande précédente est : 1
Il est possible d'affecter directement des paramètres au shell grâce à la commande
set
Une simple commande tel que :
set param1 param2 param3
initialisera automatiquement les paramètres positionnels
$1
,
$2
,
$3
avec les valeurs
param1
,
param2
,
param3
, effaçant de ce fait les anciennes valeurs si toutefois elles existaient. Les paramètres spéciaux
$#
,
$*
et
$@
sont automatiquement mis à jours en conséquence.
$ set param1 param2 param3
$ echo "Nombre de paramètres : $#"
Nombre de paramètres : 3
$ echo "Le second paramètre est : $2"
Le second paramètre est : param2
$ echo "Les paramètres sont : $@"
Les paramètres sont : param1 param2 param3
$ set pêche pomme
$ echo "Nombre de paramètres : $#"
Nombre de paramètres : 2
$ echo "Les paramètres sont : $@"
Les paramètres sont : pêche pomme
Cette fonctionnalité peut s'avérer utile dans le traitement de fichiers ligne
par ligne afin d'isoler chaque mot (champ), et d'en formater la sortie.
$ IFS=":"; set $(grep $USER /etc/passwd)
$ echo -e "Login : $1
Nom : $5
ID : $3
Group : $4
Shell : $7"
Login : jp
Nom : Jean-Philippe
ID : 500
Group : 500
Shell : /bin/bash
La commande interne
shift
permet quant à elle de décaler les paramètres. La valeur du 1er paramètre (
$1
) est remplacée par la valeur du 2nd paramètre (
$2
), celle du 2nd paramètre (
$2
) par celle du 3ème paramètre (
$3
), etc... On peut indiquer en argument (
shift [n]
) le nombre de pas (position) dont il faut décaler les paramètres.
Voilà une mise en oeuvre de l'emploi de la commande interne "shift".
#!/bin/bash
# decale_param.sh
echo
echo "Nombre de paramètres : $#"
echo "Le 1er paramètre est : $1"
echo "Le 3ème paramètre est : $3"
echo "Le 6ème paramètre est : $6"
echo "Le 10ème paramètre est : ${10}"
echo "============================================="
echo "Décalage d'un pas avec la commande "shift""
shift
echo "Nombre de paramètres : $#"
echo "Le 1er paramètre est : $1"
echo "Le 3ème paramètre est : $3"
echo "Le 6ème paramètre est : $6"
echo "Le 10ème paramètre est : ${10}"
echo "============================================="
echo "Décalage de quatre pas avec la commande "shift 4""
shift 4
echo "Nombre de paramètres : $#"
echo "Le 1er paramètre est : $1"
echo "Le 3ème paramètre est : $3"
echo "Le 6ème paramètre est : $6"
echo "Le 10ème paramètre est : ${10}"
echo
Et son résultat :
./decale_param.sh 1 2 3 4 5 6 7 8 9 10
Nombre de paramètres : 10
Le 1er paramètre est : 1
Le 3ème paramètre est : 3
Le 6ème paramètre est : 6
Le 10ème paramètre est : 10
=============================================
Décalage d'un pas avec la commande "shift"
Nombre de paramètres : 9
Le 1er paramètre est : 2
Le 3ème paramètre est : 4
Le 6ème paramètre est : 7
Le 10ème paramètre est :
=============================================
Décalage de quatre pas avec la commande "shift 4"
Nombre de paramètres : 5
Le 1er paramètre est : 6
Le 3ème paramètre est : 8
Le 6ème paramètre est :
Le 10ème paramètre est :
Voici à quoi ressemble la première étape :
OPTS=$( getopt -o h -l home,nb_fichiers: -- "$@" )
if [ $? != 0 ]
then
exit 1
fi
eval set -- "$OPTS"
Ligne 1 :
•.getopt est le nom de la commande
•.-o pour les options courtes, c'est à dire une lettre par option
•.h sera une option courte, pour afficher l'aide par exemple
•.-l pour les options longues, c'est à dire un ensemble de lettres, comme un mot
•.home sera une option longue simple
•.nb_fichiers: sera une option longue attendant un argument en paramètre
•.- - pour le comportement par défaut
Pour analyser les options, on utilise une boucle :
while true ; do
case "$1" in
-h) usage;
exit 0;;
--home) showHome;
shift;;
--nb_fichiers) echo "Afficher le nombre de fichiers dans le répertoire $2";
shift 2;;
--) shift; break;;
esac
done
Analysons cette portion de code :
•.while true; do [..] done : une simple boucle while qui sera exécutée jusqu'à ce que le script soit terminé ;
•.case $1 in [..] esac : on regarde le premier argument et on compare son résultat à l'un des cas attendus par le script ;
•.-h : si $1 vaut -h, alors on exécute la fonction usage() et on termine le script ;
•.- -home : si $1 vaut - -home, alors on exécute la fonction showHome() et on supprime l'argument $1 ;
•.- -nb_fichiers : si $1 vaut - -nb_fichiers, alors on affiche la ligne avec le nom du répertoire donné dans $2 et on supprime les arguments $1 et $2 ;
•.- - : si $1 vaut - - on supprime l'argument $1 et on effectue un break (comportement de fin attendu par case [..] esac)
Le script final, de mon cru et sous licence GPL ;), pourra ainsi ressembler à ceci :
#!/bin/bash
function usage(){
printf "Utilisation du script :\n"
printf "\t--home : affiche le chemin vers le home de l'utilisateur courant ;\n"
printf "\t--nb_fichiers repertoire : affiche le nombre de fichiers contenus dans le répertoire ;\n"
printf "\t-h : affiche ce message.\n"
}
if [ $# -eq 0 ]
then
usage
fi
function showHome(){
echo "Home de l'utilisateur $USER";
echo $HOME;
}
function nbFichiers(){
if [ -d $1 ]
then
nb=$( ls $1 | wc -l )
echo "$1 contient $nb fichiers et répertoires."
else
echo "Ce n'est pas un répertoire !"
usage
exit 0
fi
}
OPTS=$( getopt -o h -l home,nb_fichiers: -- "$@" )
if [ $? != 0 ]
then
exit 1
fi
eval set -- "$OPTS"
while true ; do
case "$1" in
-h) usage;
exit 0;;
--home) showHome;
shift;;
--nb_fichiers) echo "Afficher le nombre de fichiers et répertoires dans le répertoire $2";
nbFichiers $2;
shift 2;;
--) shift; break;;
esac
done
exit 0
Getopts est une commande qui permet de contrôler les opérateurs utilisés par une commande. Cette méthode prendra en paramètre une chaîne de caractères lui indiquant les lettres définissant les options, et pourra être parcourue dans une boucle afin de contrôler chaque argument.
Pour illustrer son utilisation, imaginons un script qui accepte deux arguments : l'un avec l'option "v", qui peut être égale à 15 ou 75 l'autre avec l'option "g". On va créer une fonction "usage" qui affiche une phrase montrant l'utilisation du script :
usage() { echo "Usage: $0 [-v <15|75>] [-p <chaîne de caractères>]" 1>&2; exit 1; }
Pour vérifier les arguments de la fonction, on va utiliser "getopts". Dans la boucle, on utilise l'instruction "case" pour vérifier la valeur de l'option saisie. Pour l'option "g", on récupère la valeur de p dans une variable grâce à la variable "OPTARG". Pour "v", on contrôle que la valeur est égale à 15 ou 75. Si ce n'est pas le cas, on appelle la fonction "usage". Pour tous les autres arguments, on les détecte avec l'opérateur "*", et on appelle la fonction "*". On décale ensuite l'ordre des paramètres avec la commande "shift". Pour vérifier que tous les paramètres sont bien saisis, on réalise un test avec l'opérateur de comparaison "-z".
while getopts ":v:g:" option; do
case "${option}" in
v)
v=${OPTARG}
((v == 15 || v == 75)) || usage
;;
g)
g=${OPTARG}
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))
if [ -z "${v}" ] || [ -z "${g}" ]; then
usage
fi
echo "v = ${v}"
echo "g = ${g}"
Voici des exemples d'utilisation du script :
# Sans paramètres, le message d'usage est affiché
./monscript.sh
Usage: ./monscript.sh [-v <15|75>] [-g <chaîne de caractères>]
#Usage avec un mauvais paramètre
./myscript.sh -h 1
Usage: ./monscript.sh [-v <15|75>] [-g <chaîne de caractères>]
# usage correct
$ ./myscript.sh -v 15 -g toto
v = 15
g = toto